<?php
// +----------------------------------------------------------------------
// | INPHP
// +----------------------------------------------------------------------
// | Copyright (c) 2021 https://inphp.cc All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://opensource.org/licenses/MIT )
// +----------------------------------------------------------------------
// | Author: lulanyin <me@lanyin.lu>
// +----------------------------------------------------------------------
namespace Inphp\Service;

use Inphp\Cache;
use Inphp\Config;
use Inphp\Service\Object\Module;
use Inphp\Service\Object\RouterStatus;
use Inphp\Util\Arr;

/**
 * 模块化处理
 * Class Modular
 * @package Inphp
 */
class Modular
{
    /**
     *
     * 模块化配置
     * @var array
     */
    public static array $config = [];

    /**
     * 模块化缓存
     * @var array
     */
    public static array $modules_cache = [];

    /**
     * 获取配置
     * @param string|null $key
     * @return mixed
     */
    public static function getConfig(string|null $key = null) : mixed
    {
        if(empty(self::$config)){
            self::$config = Config::get('modules');
        }
        if(is_null($key)){
            return self::$config;
        }
        return Arr::get(self::$config, $key);
    }

    /**
     * 获取模块列表
     * @return array
     */
    public static function getModuleNameList() : array
    {
        return self::getConfig("list");
    }

    /**
     * 获取所有的模块
     * @return array
     */
    public static function getModules() : array
    {
        $nameList = self::getModuleNameList();
        $modules = [];
        foreach ($nameList as $name)
        {
            $modules[$name] = self::getModule($name);
        }
        return $modules;
    }

    /**
     * 获取模块配置
     * @param $name
     * @return Module|null
     */
    public static function getModule($name) : Module | null
    {
        $module = self::$modules_cache[$name] ?? null;
        if(is_null($module)){
            $modules_config = self::getConfig();
            $root = ROOT."/".$modules_config['root'];
            $file = $root."/".$name."/config.json";
            if(is_file($file)){
                $content = file_get_contents($file);
                $module_config = json_decode($content, true);
                if(null !== $module_config){
                    $module = new Module($module_config);
                    self::$modules_cache[$name] = $module;
                }
            }
        }

        return $module;
    }

    /**
     * 模块初始化，用于加载各模块需要引入的文件
     */
    public static function init(){
        $modules = self::getModules();
        foreach ($modules as $module){
            //加载文件
            $require_files = $module->config['require'] ?? [];
            foreach ($require_files as $file)
            {
                $file = $module->file_path."/".$file;
                if(is_file($file)){
                    require_once $file;
                }
            }
        }
    }

    /**
     * 模块化处理，如果已有路由缓存，不会执行
     * @param string $host                      域名
     * @param string $uri                       路径
     * @param string|null $request_method       请求类型  GET,POST
     * @param string $group                     服务类型
     * @return RouterStatus
     */
    public static function process(string $host, string $uri = '', string|null $request_method = null, string $group = Service::HTTP) : RouterStatus
    {
        //模块化配置
        $config = self::getConfig();
        //模块化路由入口（存放模块的文件夹位置）
        $home = Arr::get($config, "home", "modules\\");
        //根据 host 识别模块
        $domains = Arr::get($config, "domains", []);
        if(!empty($domains) && ($module_name = array_search($host, $domains))){
            //从列表中匹配，找到入口
            $pathArray = !empty($uri) ? explode("/", $uri) : ["index", "index"];
            //记录长度，以便于后续的智能判断使用
            $pathLen = count($pathArray) + 1;
            //至少长度为2
            if($pathLen < 3){
                $pathArray = array_pad($pathArray, 2, "index");
            }
            //将module名称组合到第一截
            $pathArray = array_merge([$module_name], $pathArray);
        }else{
            //常规的入口
            $listPath = array_keys($config['list']);
            //拆分请求路径
            $pathArray = !empty($uri) ? explode("/", $uri) : [
                $config['default'], "index", "index"
            ];
            //判断第一截是否在入口列表中
            if(!in_array($pathArray[0], $listPath)){
                //不在，则添加到默认入口
                $pathArray = array_merge([$config['default']], $pathArray);
            }
            //替换为实际的命名空间名称
            $pathArray[0] = $config["list"][$pathArray[0]];
            //记录长度，以便后续的智能匹配使用
            $pathLen = count($pathArray);
            //如果长度少于3
            if($pathLen < 3){
                $pathArray = array_pad($pathArray, 3, "index");
            }
            //记录模块名
            $module_name = $pathArray[0];
        }
        //模块化入口
        //获取模块化配置
        $module = self::getModule($module_name);
        if(is_null($module)){
            return new RouterStatus([
                "status"    => 404,
                "message"   => 'undefined module '.$module_name,
                "state"     => 'modular'
            ]);
        }else{
            //静态全路径匹配，不使用控制器
            $close_controller = isset($module->http_config['controller']) && $module->http_config['controller'] == false;
            //将模块保存到上下文，后续可以使用
            Context::setModule($module);
            //使用模块继续处理...
            //获取入口
            $module_router_home = $module->getHome($group);
            //截掉第一个，第一个是模块名称
            $pathArray = array_slice($pathArray, 1);
            //当前请求版块
            $part = $pathArray[0];
            //模块的所有可用路由列表
            $router_list = $module->getRouterList($group);
            //默认版块，
            $default_router = $module->getDefaultRouter($group);
            //填的是 "/" 或者 ""，会自动处理成 "/"
            if(empty($router_list)){
                //则说明直接默认使用入口，不区分版块
                $home_val = $module->getHomeValue($group);
                $pathArray = array_merge([$home_val], $pathArray);
                $module_router_home = $module->namespace;
                //
                //echo "01: ".PHP_EOL;print_r($pathArray);
            }else{
                //由于多了一层模块，需要减1
                $pathLen -= 1;
                //区分版块
                if(isset($router_list[$part])){
                    $pathArray[0] = $router_list[$part];
                }else{
                    //添加默认
                    $part = $router_list[$default_router];
                    $pathArray = array_merge([$router_list[$default_router]], $pathArray);
                }
                //长度少于3
                if(count($pathArray) < 3){
                    $pathArray = array_pad($pathArray, 3, "index");
                }

            }
            //视图
            $view_dir = $view_suffix = '';
            if($group == Service::HTTP){
                $view_dir = $module->getViewDir();
                $view_dir = strrchr($view_dir, "/") == "/" ? $view_dir : "{$view_dir}/";
                $view_suffix = $module->getViewSuffix();
                //如果配置了全模块不使用控制器，则直接匹配路径的模板文件
                if($close_controller && !stripos($module->getResponseContentType($part),"json")){
                    //处理视图后缀
                    $view_suffix = stripos($view_suffix, ".") === 0 ? $view_suffix : ".{$view_suffix}";
                    $view_dir = strrchr($view_dir, "/") == "/" ? $view_dir : "{$view_dir}/";
                    $view_dir = str_replace("//", "/", $view_dir);
                    //视图文件
                    $pathArray = empty($router_list) ? array_slice($pathArray, 1) : $pathArray;
                    $view_file = join("/", $pathArray);
                    $view_file = strrchr($view_file, $view_suffix) === $view_suffix ? $view_file : "{$view_file}{$view_suffix}";
                    $slice = false;
                    if(!file_exists($view_dir.$view_file)){
                        $slice = true;
                        $view_file = join("/", array_slice($pathArray, 0, -1));
                        $view_file = strrchr($view_file, $view_suffix) === $view_suffix ? $view_file : "{$view_file}{$view_suffix}";
                    }
                    if(file_exists($view_dir.$view_file)){
                        $pathArray = $slice ? array_slice($pathArray, 0, -1) : $pathArray;
                        //匹配到视图文件
                        $path = empty($router_list) ? "" : reset($pathArray);
                        return new RouterStatus([
                            "status"    => 200,
                            "state"     => 'html',
                            "controller"=> null,//class_exists($controller) ? $controller : null,
                            "method"    => null,//class_exists($controller) ? "index" : null,
                            "view_dir"  => $view_dir.$path,
                            "path"      => $path,
                            "view"      => empty($router_list) ? $view_file : substr($view_file, strlen($path) + 1)
                        ]);
                    }else{
                        return new RouterStatus([
                            "status"    => 404,
                            "message"   => 'view file '.$view_file." not exists",
                            "state"     => 'modular'
                        ]);
                    }
                }
            }
            //返回匹配的数据
            return Router::match($module_router_home, $pathArray, $module->getResponseContentType($part), $view_dir, $view_suffix, $group, empty($router_list));
        }
    }

    /**
     * 将地址转换为正确的地址
     * @param string $url
     * @param string|null $moduleName
     * @return string
     */
    public static function parseUrl(string $url, string|null $moduleName = null) : string
    {
        //添加自动识别
        if(is_null($moduleName)){
            $self_module = Context::getModule();
            if(!empty($self_module)){
                $moduleName = $self_module->id;
            }
        }
        $url_query = stripos($url, "?") !== false ? explode("?", $url, 2) : [$url, ""];
        $url = $url_query[0];
        $query = $url_query[1];
        $keyName = "url_".md5($moduleName."_".$url);
        $url_cache = self::getConfig("url_cache") ?? false;
        $value = $url_cache ? Cache::get($keyName) : null;
        if(!empty($value)){
            return $value.(!empty($query) ? "?{$query}" : "");
        }
        if(stripos($url, "./") === 0){
            //使用当前的模块
            $str_array = explode("/", $url);
            //取path
            $path = $str_array[1] ?? null;
            //
            $url_array = count($str_array) > 2 ? array_slice($str_array, 2) : [];
        }elseif(stripos($url, "/") === 0){
            //使用外部模块
            $str_array = explode("/", $url);
            $moduleName = $str_array[1];
            $path = $str_array[2] ?? null;
            $url_array = count($str_array) > 3 ? array_slice($str_array, 3) : [];
        }else{
            return $url;
        }
        $module = self::getModule($moduleName);
        $http = $module->http_config;
        $list = $http['list'] ?? [];
        $default = $http['default'] ?? '';
        //反转
        if(!empty($list)){
            $flip = array_flip($list);
            if(isset($flip[$path])){
                //存在path，替换
                $path = $flip[$path];
            }
        }else{
            //空的
        }
        $moduleNames = self::getModuleNameList();
        //反转
        $flip = array_flip($moduleNames);
        if(isset($flip[$moduleName])){
            $moduleName = $flip[$moduleName];
        }
        $value = "/".$moduleName.(!empty($path) ? ("/".$path) : "").(!empty($url_array) ? ("/".join("/", $url_array)) : "");
        if($url_cache){
            Cache::set($keyName, $value);
        }
        return $value.(!empty($query) ? "?{$query}" : "");
    }

    /**
     * 模块数据刷新
     */
    public static function refresh(){
        //配置清空，按需重新加载
        self::$config = [];
        //
        self::$modules_cache = [];
        //清除
        Router::clean();
    }

    /**
     * 获取模块的配置
     * @param string $name
     * @param string|null $path
     * @return mixed
     */
    public static function getModuleConfig(string $name, string|null $path = null): mixed
    {
        if($module = self::getModule($name))
        {
            return Arr::get($module->config, $path);
        }
        return null;
    }
}